/*
 *  Copyright (c) 2022 ZhuHai Jieli Technology Co.,Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include "efuse.h"
#include "typedef.h"
#include "boot.h"
#include "power/p33.h"

__attribute__((always_inline_when_const_args)) u16 get_chip_id(void)
{
    return boot_info.chip_id;
}

__attribute__((always_inline_when_const_args)) u16 get_vbat_trim(void)
{
    return boot_info.trim_value & 0xf;
}
__attribute__((always_inline_when_const_args)) u16 get_vbg_trim(void)
{
    return (boot_info.trim_value >> 4) & 0x3f;
}

struct efuse_config {
    u16 reserve : 7;
    u16 bit0 : 3;
    u16 bit1 : 3;
    u16 bit2 : 3;
};

struct efuse_page1 {
    union {
        struct efuse_config cfg;
        u32 value;
    } u;
};

static const u8 sysdvdd_config_table[] = {
    9, 8, 7, 6, 5, 4, 3, 3,
};

static void udelay(u32 nus)
{
    JL_TIMER0->CON = BIT(14);
    JL_TIMER0->CNT = 0;
    JL_TIMER0->PRD = 48000000L / 1000000 * nus; // 1us
    JL_TIMER0->CON = BIT(0); // sys clk
    while ((JL_TIMER0->CON & BIT(15)) == 0);
    JL_TIMER0->CON = BIT(14);
}

#define     EFUSE_IO_DEBUG_0(i, x)       // {JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT &= ~BIT(x);}
#define     EFUSE_IO_DEBUG_1(i, x)       // {JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT |= BIT(x);}

#define EFUSE_REF 0b10
u32 p33_rd_page(u8 page)
{
#if P33_EFUSE_DEBUG
    return p33_efuse[page][0];
#else
    u8 addr;
    u32 rdat = 0;
    u8 tmp;
    u32 tmp32;
    for (addr = 0; addr < 4; addr++) {
        p33_tx_1byte(P3_EFUSE_CON0, (page << 6));
        p33_tx_1byte(P3_EFUSE_CON1, BIT(1) | (addr << 2) | EFUSE_REF << 6); // read enable
        udelay(50);
        tmp = p33_rx_1byte(P3_EFUSE_RDAT);
        tmp32 = tmp;
        tmp32 <<= 8 * addr;
        rdat |= tmp32;
        p33_and_1byte(P3_EFUSE_CON1, ~BIT(1)); // read disable
    }
    return rdat;
#endif
}

__attribute__((always_inline_when_const_args)) u8 get_sysdvdd_trim(void)
{
    struct efuse_page1 page1;

    u8 sys_dvdd = 0;

    page1.u.value = p33_rd_page(1);

    if (page1.u.cfg.bit0 == 0x7) {
        sys_dvdd |= BIT(0);
    }
    if (page1.u.cfg.bit1 == 0x7) {
        sys_dvdd |= BIT(1);
    }
    if (page1.u.cfg.bit2 == 0x7) {
        sys_dvdd |= BIT(2);
    }

    return sysdvdd_config_table[sys_dvdd];
}

__attribute__((always_inline_when_const_args)) u32 get_chip_version(void)
{
    u32 id = JL_SYSTEM->CHIP_ID;
    return id;
}

void efuse_test(void)
{
    printf("get_chip_id:   %x\n", get_chip_id());
    printf("get_vbat_trim: %x\n", get_vbat_trim());
    printf("get_vbg_trim:  %x\n", get_vbg_trim());
}
